Svg Icon 图标组件开发
Why ? 🤔
为什么使用 SVG
而不是 IconFont
,可以看这篇文章 Inline SVG vs Icon Fonts
IconFont
主要有以下几点劣势:
- 浏览器将其视为文字进行抗锯齿优化,有时得到的效果并没有想象中那么锐利。 尤其是在不同系统下对文字进行抗锯齿的算法不同,可能会导致显示效果不同
IconFont
作为一种字体,Icon 显示的大小和位置可能要受到font-size
、line-height
、word-spacing
等等 CSS 属性的影响。 Icon 所在容器的 `CSS` 样式可能对 `Icon` 的位置产生影响,调整起来很不方便- 使用上存在不便。首先,加载一个包含数百个图标的
IconFont
,却只使用其中几个,非常浪费加载时间。 自己制作IconFont
以及把多个IconFont
中用到的图标整合成一个Font
也非常不方便 - 如果想实现最大程度的浏览器支持,可能要提供至少四种不同类型的字体文件。包括
TTF
、WOFF
、EOT
以及一个使用SVG
格式定义的字体 - 网络延时会导致 Icon 会先加载出来一个
string
Svg Icon
的优劣势:
- 完全离线化使用,不需要从 CDN 下载字体文件,图标不会因为网络问题呈现方块,也无需字体文件本地部署
- 在低端设备上 SVG 有更好的清晰度
- 支持多色图标
- 对于内建图标的更换可以提供更多 API,而不需要进行样式覆盖
劣势:兼容性(其实目前浏览器兼容性已经不错 查看兼容性)
What ? 🧐
实现原理
- svg 图标比较小,而且都是可读的 xml 文本,我们把它直接放在项目中即可,通过
vite-plugin-svg-icons
插件生成 svg 雪碧图,实现自动引入 - 插件会自动将所有 svg 图片加载到 HTML 中。并且每一个 svg 将会被过滤去无用的信息数据。让 svg 达到最小的值。之后使用 svg 图片就只需要操作 DOM 即可,而不需要发送 http 请求
- 利用 svg 的 symbol 元素,将每个 icon 包括在 symbol 中
- 再通过
<use xlink:href="symbolId"/>
来使用所需的 icon
优点:
- 解决各种版本 iconfont 私有图标库问题
- 每个 SVG 图标都可以更改大小颜色
- 在页面中使用
<svgIcon name="home" />
,代码清爽
How ? 😉
配置
vite.config.ts
文件安装
vite-plugin-svg-icons
插件后编辑 Vite 配置文件:文档- iconDirs 项目中 svg 图标存放路径,这里设置为 assets 下的 icons 文件夹
- symbolId 图标的唯一 id 标识,dir 代表图标的文件夹名称
- 比如在 icons 中将登录模块相关的图标都整理到 login 文件夹中,则这里的 dir 就是 login
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' // 省略其他部分代码 plugins: [ createSvgIconsPlugin({ iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], // 图标 ID 样式 dir 代表文件的文件夹名称 symbolId: 'svg-icon-[dir]-[name]', }), ]
虽然用文件夹来区分已经可以很大程度避免重名问题了,但是也会出现
iconDirs
包含多个文件夹,且文件名一样的 svg,这个需要开发者自己规避下。配置
main.ts
import 'virtual:svg-icons-register'
封装 SvgIcon 图标组件
<template> <div :class="wrapperColor"> <svg aria-hidden="true" :width="width || size" :height="height || size" :class="className"> <use :xlink:href="symbolId" :fill="color" /> </svg> </div> </template> <script setup lang="ts" name="SvgIcon"> interface Props { prefix?: string name?: string color?: string size?: number width?: number height?: number className?: string dir?: string } const props = withDefaults(defineProps<Props>(), { prefix: 'svg-icon', name: '', color: '#fff', size: 16, width: 16, height: 16, className: 'svg-icon', dir: '', }) const wrapperColor = computed(() => `text-color-[${props.color}]`) const symbolId = computed(() => { return props.dir?.length ? `#${props.prefix}-${props.dir}-${props.name}` : `#${props.prefix}-${props.name}` }) </script> <style lang="scss" scoped> .svg-icon { vertical-align: -0.15em; fill: currentColor; overflow: hidden; } </style>
- 通过 wrapperColor (Windi Css 功能类)将颜色设置给 svg 文件,默认白色
使用说明
<svg-icon name="user" color="#2395f1" :size="30"/> <svg-icon name="action" color="#d9363e" :width="40" :height="30"/> <div class="text-green-500"> 文本 <svg-icon name="robot" dir="timeline" className="pr-10px" @click="test"/> <svg-icon name="action" color="#d9363e" /> </div>
- name 就是放置在 @/assets/icons 文件夹里的图标文件名
- color 颜色填充,使用此项会默认覆盖图标颜色
- size 为图标的宽高值,也可以单独声明宽高的值,优先级更高
- 如果想实现多个图标批量设置色值,可以通过给父元素设置 color 色值实现,不过优先级比单独给组件设置 color 要低
- 如果图标是放在指定的文件夹内,需要使用 dir 标识
- 图标的其他样式,可以通过额外设置 className 实现
- 因为图标组件中是用 div 标签包裹 svg 的,可以直接给组件绑定事件
注意事项: 🚨
- 单色图标需要设计同学导出
fill ="currentColor"
属性的 svg 图片才能实现自定义颜色 - 如果 UI 没有提供,可以在
Figma
中安装 Svg Export 插件,将导出配置中的Use currentColor as fill
和Remove all fills
选项打开